﻿using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using CloudShot.Utils;

namespace CloudShot.Editor.CanvasFigures
{
  public class CanvasText : CanvasRectangle
  {
    public string Text { get; set; }
    public Color TextColor { get; set; }
    public float FontSize { get; set; }
    public bool IsItalic { get; set; }
    public bool IsBold { get; set; }
    public bool IsUnderline { get; set; }
    public StringAlignment TextAlignment { get; set; }

    public Font GetFont()
    {
      //return new Font("Calibri", FontSize, GetFontStyle());
      return new Font("Comic Sans MS", FontSize, GetFontStyle());
    }

    public CanvasText(string text, int x, int y, int width, int height) : base(x, y, width, height)
    {
      Text = text;
      FontSize = 32;
      TextColor = Color.FromArgb(246, 149, 69);
      TextAlignment = StringAlignment.Center;
    }

    private CanvasText() {}

    public override void Draw(Graphics graphics)
    {
      base.Draw(graphics);

      DrawTextByGraphics(graphics, false);
      //DrawTextByTextRenderer(graphics, false);
    }

    protected override CanvasObject CloneIntrenal()
    {
      var clone = new CanvasText
      {
        Text = Text,
        TextColor = TextColor,
        FontSize = FontSize,
        IsItalic = IsItalic,
        IsBold = IsBold,
        IsUnderline = IsUnderline,
        TextAlignment = TextAlignment
      };

      SetCloneProperties(clone);
      return clone;
    }

    private static Rectangle GetTextBounds(Rectangle rect, SizeF textSize)
    {
      int textX = rect.X;
      int textY = rect.Height > textSize.Height ? (int)(rect.Y + (rect.Height - textSize.Height) / 2) : rect.Y;
      int textWidth = textSize.Width > rect.Width ? (int)textSize.Width : rect.Width;
      int textHeight = (int)textSize.Height;

      return new Rectangle(textX, textY, textWidth, textHeight);
    }

    private void DrawTextByGraphics(Graphics graphics, bool isDebugRender)
    {
      using (var fontBrush = new SolidBrush(TextColor))
      using (Font font = GetFont())
      {
        Rectangle rect = Rectangle.GetNormalizedRectangle();

        var format = new StringFormat(StringFormat.GenericDefault)
        {
          Alignment = TextAlignment
        };

        SizeF textSize = graphics.MeasureString(Text, font, rect.Width, format);
        Rectangle textRect = GetTextBounds(rect, textSize);

        GraphicsState state = graphics.Save();
        graphics.TextContrast = 0;
        graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

        //clip text by rectangle
        Region backupClipRegion = graphics.Clip;
        graphics.Clip = new Region(rect);

        //draw shadow (looks much better than FillPath)
        graphics.TranslateTransform(0.5f, 0.5f);
        graphics.DrawString(Text, font, new SolidBrush(Color.FromArgb(25, Color.Black)), textRect, format);
        graphics.ResetTransform();
        
        //draw text
        graphics.DrawString(Text, font, fontBrush, textRect, format);

        if (isDebugRender)
        {
          graphics.DrawRectangle(Pens.Magenta, new Rectangle(textRect.X, textRect.Y, (int)textSize.Width, (int)textSize.Height)); //for debug
          graphics.DrawRectangle(Pens.Red, textRect); //for debug  
        }

        //restore graphics
        graphics.Clip = backupClipRegion;
        graphics.Restore(state);
      }
    }

    /// <summary>
    /// Draw with TextRenderer class, better by performance, more accurate text measuring, better compatibility with localization, native, 
    /// but not supports transparency for foreColor.
    /// 
    /// I will save this code for the future.
    /// </summary>
    private void DrawTextByTextRenderer(Graphics graphics, bool isDebugRender)
    {
      const int padding = 4;
      TextFormatFlags textFormatFlags = TextFormatFlags.PathEllipsis | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.WordBreak
                                        | TextFormatFlags.PreserveGraphicsTranslateTransform | TextFormatFlags.NoPadding;

      switch (TextAlignment)
      {
        case StringAlignment.Near:
          textFormatFlags |= TextFormatFlags.Left;
          break;
        case StringAlignment.Center:
          textFormatFlags |= TextFormatFlags.HorizontalCenter;
          break;
        case StringAlignment.Far:
          textFormatFlags |= TextFormatFlags.Right;
          break;
      }

      using (var fontBrush = new SolidBrush(TextColor))
      using(Font font = GetFont())
      {
        Rectangle rect = Rectangle.GetNormalizedRectangle();

        if (rect.Width > (padding * 4))
          rect.Inflate(-padding, 0);

        SizeF textSize = TextRenderer.MeasureText(graphics, Text, font, new Size(rect.Width, int.MaxValue), textFormatFlags);
        Rectangle textRect = GetTextBounds(rect, textSize);

        if (isDebugRender)
        {
          graphics.DrawRectangle(Pens.Magenta, new Rectangle(textRect.X, textRect.Y, (int)textSize.Width, (int)textSize.Height)); //for debug
          graphics.DrawRectangle(Pens.Red, textRect); //for debug
        }

        GraphicsState state = graphics.Save();
        graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
        Region backupClipRegion = graphics.Clip;
        graphics.Clip = new Region(rect);

        //graphics.TranslateTransform(1f, 1f);
        //TextRenderer.DrawText(graphics, Text, font, textRect, Color.FromArgb(25, Color.Black), textFormatFlags);//not supported transparency for fore color =(
        //graphics.ResetTransform();
        TextRenderer.DrawText(graphics, Text, font, textRect, fontBrush.Color, textFormatFlags);

        graphics.Clip = backupClipRegion;
        graphics.Restore(state);
      }
    }

    private FontStyle GetFontStyle()
    {
      var style = FontStyle.Regular;

      if (IsBold)
        style = FontStyle.Bold;

      if (IsItalic)
        style = style | FontStyle.Italic;

      if (IsUnderline)
        style = style | FontStyle.Underline;

      return style;
    }
  }
}